home *** CD-ROM | disk | FTP | other *** search
/ MACD 5 / MACD 5.bin / workbench / tools / czesc_1 / cpdist / cpdist.c < prev    next >
C/C++ Source or Header  |  1994-01-12  |  14KB  |  556 lines

  1. /*
  2.  *  CPDIST.C
  3.  */
  4.  
  5. /*
  6.  * (c)Copyright 1992-93 by Tobias Ferber.
  7.  *
  8.  * This file is part of CPDIST.
  9.  *
  10.  * CPDIST is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published
  12.  * by the Free Software Foundation; either version 1 of the License,
  13.  * or (at your option) any later version.
  14.  *
  15.  * CPDIST is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with CPDIST; see the file COPYING.  If not, write to
  22.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  */
  24.  
  25. #include <ctype.h>
  26. #include <string.h>
  27. #include <stdlib.h>
  28. #include <stdio.h>
  29. #include "cpdist.h"
  30. #include "filecopy.h"
  31.  
  32. /* If you don't want CPDIST to interact with the user's keyboard via
  33.  * getkey() then you should compile this file with the -DNO_GETKEY option.
  34.  * copy_files() (the only function that uses getkey() will then prompt
  35.  * waiting for a string instead of a character
  36.  */
  37.  
  38. #ifndef NO_GETKEY
  39. #include "getkey.h"
  40. #endif
  41.  
  42. static char rcs_id[]= "$VER: $Id$";
  43.  
  44. /* scanner modes */
  45.  
  46. typedef enum { outer_mode,    /* reading word seperating chars */
  47.                word_mode,     /* collecting chars for a word */
  48.                string_mode,   /* collecting chars for a string */
  49.                remark_mode,   /* waiting for the end of line */
  50.                comment_mode,  /* waiting for the matching '*' and '/' */
  51.                return_mode,   /* word or string complete */
  52.                error_mode     /* an error occured! return NULL */
  53.              } smode_t;
  54.  
  55.  
  56. char *getword(FILE *fp)
  57. /* This function will skip everything up to the beginning of the next word
  58.  * that is not inside of a comment and then copy the following word into a
  59.  * static buffer and terminate it with a '\0' character.  A pointer to this
  60.  * buffer will be returned.
  61.  * A comment is (as in C) everything that stands between a '/' + '*' token
  62.  * and the matching '*' + '/'.  C++ like comments (named remarks here) are
  63.  * also suported.  (They begin with two slashes '/' + '/' and end with the
  64.  * end of the line they appear in.)
  65.  * Note:  This file will use lerror() to report error messages to the user.
  66.  * If anyone else but this function will read from the given file 'fp' then
  67.  * the line numbers reported by lerror() may become wrong! */
  68. {
  69.   static char buf[128];      /* should be enough for _one_ word */
  70.   static int line= 1;        /* line number */
  71.   int n=0;                   /* #of chars in buf[] */
  72.   unsigned char c;           /* currently read character */
  73.  
  74.   smode_t smode= outer_mode; /* scanner mode */
  75.  
  76.   while(smode!=return_mode && smode!=error_mode && !feof(fp))
  77.   {
  78.     c= fgetc(fp);
  79.  
  80.     if(feof(fp) && c!=(unsigned char)EOF)
  81.     { echo("In module " __FILE__ ": feof()==TRUE but fgetc()==0x%02x",
  82.         (int)(c&0xFF));
  83.       c= (unsigned char)EOF;
  84.     }
  85.  
  86.     switch(c)
  87.     {
  88.       case ' ': case '\t': case ':':
  89.         switch(smode)
  90.         {
  91.           case word_mode:
  92.             if(c==':') buf[n++]=':';
  93.             buf[n++]= '\0';
  94.             smode= return_mode;
  95.             break;
  96.           case string_mode:
  97.             buf[n++]= c;
  98.             break;
  99.         }
  100.         break;
  101.  
  102.       case '\n': case '\r':
  103.         switch(smode)
  104.         {
  105.           case word_mode:
  106.             buf[n++]= '\0';
  107.             smode= return_mode;
  108.             break;
  109.           case string_mode:
  110.             lerror(line,"unterminated string; missing quotes `\"'");
  111.             smode= error_mode;
  112.             break;
  113.           case remark_mode:
  114.             smode= outer_mode;
  115.             break;
  116.         }
  117.         line++;
  118.         break;
  119.  
  120.       case '\"':
  121.         switch(smode)
  122.         {
  123.           case outer_mode:
  124.             smode= string_mode;
  125.             break;
  126.           case string_mode:
  127.             buf[n++]= '\0';
  128.             smode= return_mode;
  129.             break;
  130.           case word_mode:
  131.             buf[n++]= '\0';
  132.             ungetc(c,fp);
  133.             smode= return_mode;
  134.             break;
  135.         }
  136.         break;
  137.  
  138.       case '/':
  139.         switch(smode)
  140.         {
  141.           case outer_mode:
  142.             switch(c= fgetc(fp))
  143.             {
  144.               case '*':
  145.                 smode= comment_mode;
  146.                 break;
  147.               case '/':
  148.                 smode= remark_mode;
  149.                 break;
  150.               default:
  151.                 ungetc(c,fp);
  152.                 break;
  153.             }
  154.             break;
  155.           case word_mode:
  156.           case string_mode:
  157.             buf[n++]= c;
  158.             break;
  159.         }
  160.         break;
  161.  
  162.       case '*':
  163.         switch(smode)
  164.         {
  165.           case comment_mode:
  166.             switch(c= fgetc(fp))
  167.             {
  168.               case '/':
  169.                 smode= outer_mode;
  170.                 break;
  171.               default:
  172.                 ungetc(c,fp);
  173.                 break;
  174.             }
  175.             break;
  176.           case word_mode:
  177.           case string_mode:
  178.             buf[n++]= c;
  179.             break;
  180.         }
  181.         break;
  182.  
  183.       case (unsigned char)EOF:
  184.         switch(smode)
  185.         {
  186.           case word_mode:
  187.             buf[n++]= '\0';
  188.             smode= return_mode;
  189.             break;
  190.           case string_mode:
  191.             lerror(line,"unterminated string at EOF; closing quotes `\"' missing");
  192.             smode= error_mode;
  193.             break;
  194.           case comment_mode:
  195.             lerror(line,"unterminated comment at EOF; closing `*/' missing");
  196.             smode= error_mode;
  197.             break;
  198.         }
  199.         break;
  200.  
  201.       default:
  202.         switch(smode)
  203.         {
  204.           case outer_mode:
  205.             smode= word_mode;
  206.             /* fall through */
  207.           case word_mode:
  208.           case string_mode:
  209.             buf[n++]= c;
  210.             break;
  211.         }
  212.         break;
  213.     }
  214.   }
  215.  
  216.   buf[n]= '\0'; /* better add one more... */
  217.  
  218.   return (smode==error_mode) ? (char *)0L : buf;
  219. }
  220.  
  221. typedef struct fnode {
  222.   struct fnode *next;
  223.   char *fname;
  224. } fnode;
  225.  
  226.  
  227. /* collect states */
  228.  
  229. typedef enum { okay,
  230.                no_such_file,
  231.                out_of_memory,
  232.                syntax_error,
  233.              } state_t;
  234.  
  235. fnode *collect_files(FILE *fp)
  236. /* Here we collect the filenames in the given distfile pointer 'fp' and
  237.  * add them to a list of fnode structures.  This function reports errors
  238.  * (if there are any) via the echo() function (see main.c or so) */
  239. {
  240.   char *fname;
  241.  
  242.   fnode *flist = (fnode *)0L,
  243.         *last  = (fnode *)0L;
  244.  
  245.   int do_collect= 1;    /* collect files (1) or skip until next label (0) */
  246.   int files= 0;         /* #of collected files */
  247.   int errors= 0;        /* #of syntax errors in distfile */
  248.   int black_sheeps= 0;  /* #of inaccessable items (e.g. dirs) */
  249.  
  250.   state_t state= okay;
  251.  
  252.   do
  253.   { fname= getword(fp);
  254.  
  255.     if(fname && *fname)
  256.     {
  257.       if(fname[strlen(fname)-1]==':')
  258.         do_collect= ln_member(fname);
  259.  
  260.       else if(do_collect)
  261.       {
  262.         if(CP_CHECKEXISTS)
  263.         {
  264.           FILE *tf;
  265.           if( tf= fopen(fname,"r") )
  266.             fclose(tf);
  267.           else
  268.           { perror(fname);
  269.             black_sheeps++;
  270.             state= no_such_file;
  271.           }
  272.         }
  273.  
  274.         if(state != no_such_file)
  275.         {
  276.           fnode *this;
  277.  
  278.           if( this= (fnode *)malloc(sizeof(fnode)) )
  279.           {
  280.             if( this->fname= (char *)malloc((strlen(fname)+1)*sizeof(char)) )
  281.             {
  282.               strcpy(this->fname, fname);
  283.               this->next= (fnode *)0L;
  284.  
  285.               if(last) last->next= this;
  286.               else flist= this;
  287.               last= this;
  288.               files++;
  289.             }
  290.             else
  291.             { free(this);
  292.               /* this= (fnode *)0L; */
  293.               state= out_of_memory;
  294.             }
  295.           }
  296.           else state= out_of_memory;
  297.         }
  298.       }
  299.     }
  300.     else if(!feof(fp))
  301.     { errors++;
  302.       if(!CP_IGNOREERRORS)
  303.         state= syntax_error;
  304.     }
  305.  
  306.     if((state==no_such_file) && CP_KEEPGOING)
  307.       state= okay;
  308.  
  309.   } while((state==okay) && !feof(fp));
  310.  
  311.   if(state!=okay)
  312.   {
  313.     /* purge the file list */
  314.     while(flist)
  315.     { last= flist;
  316.       flist= flist->next;
  317.       if(last->fname)
  318.         free(last->fname);
  319.       free(last);
  320.     }
  321.  
  322.     if(state==out_of_memory)
  323.       echo("Ran out of memory after having collected %d %s",
  324.         files, (files==1) ? "filename" : "filenames");
  325.     echo("going down.");
  326.   }
  327.  
  328.   if(errors && CP_IGNOREERRORS)
  329.     echo("%d %s -- You should revise your Distfile...",
  330.       errors, (errors==1) ? "Error" : "Errors");
  331.  
  332.   if(black_sheeps && CP_KEEPGOING)
  333.     echo("Couldn't access %d %s",
  334.       black_sheeps, (black_sheeps==1) ? "item" : "items");
  335.  
  336.   return flist;
  337. }
  338.  
  339. #ifdef STRIP_PATHNAMES
  340. char *strfn(char *s)
  341. /* returns the filename w/o leading pathname */
  342. { char *t;
  343.   for(t=s; *s!='\0'; s++)
  344.     if(*s==':' || *s=='/' || *s=='\\')
  345.       t= &s[1];
  346.   return t;
  347. }
  348. #else
  349. #define strfn(s) (s)
  350. #endif /* STRIP_PATHNAMES */
  351.  
  352.  
  353. int copy_files(fnode *flist, char *pname)
  354. /* Here we copy all files in 'flist' to the given path 'pname'.
  355.  * The destination filename will be generated by concatenation of
  356.  * 'pname' and the actual filename in a node of 'flist'.  So 'pname'
  357.  * must have a trailing slash '/', colon ':' or backslash '\' or
  358.  * whatever.
  359.  * While copying the files, all nodes in flist will be free()d. */
  360. {
  361.   char outname[MAXIMUM_PATHNAME_LENGTH];
  362.   char *catpoint;
  363.   int numfiles= 0;
  364.   long numbytes= 0;
  365.   int err= 0;
  366.  
  367.   strcpy(outname, pname);
  368.   catpoint= &outname[strlen(outname)];
  369.  
  370.   if(!CP_DRYRUN)
  371.     (void)fc_setbuf(global_buffersize);
  372.  
  373.   while(flist && !err)
  374.   {
  375.     int dothis= 1;       /* 1=copy/replace, 0=skip, -1=??? */
  376.  
  377.     fnode *this= flist;
  378.     flist= flist->next;
  379.  
  380.     if(this->fname)
  381.     {
  382.       strcpy(catpoint, strfn(this->fname));
  383.  
  384.       if(!CP_REPLACEALL && !CP_DRYRUN)
  385.       {
  386.         FILE *fp;
  387.         if(fp= fopen(outname,"r"))
  388.         {
  389.           int key;
  390.           fclose(fp);
  391.  
  392.           fprintf(stderr,"%s already exists.  replace it? (y/n/a/q) ",outname);
  393.           fflush(stderr);
  394.  
  395.           do {
  396.  
  397. #ifdef NO_GETKEY
  398.             char answerbuf[10];
  399.             fgets(answerbuf, sizeof(answerbuf), stdin);
  400.             key= (int)answerbuf[0];
  401.  
  402. #else /* !NO_GETKEY */
  403.             key= (int)getkey();
  404.  
  405. #endif /* NO_GETKEY */
  406.  
  407.             switch( key )
  408.             {
  409.               case 'y': case 'Y':
  410. #ifndef NO_GETKEY
  411.                 puts("yes");
  412. #endif
  413.                 dothis= 1;
  414.                 break;
  415.               case 'n': case 'N':
  416. #ifndef NO_GETKEY
  417.                 puts("no");
  418. #endif
  419.                 dothis= 0;
  420.                 break;
  421.               case 'a': case 'A':
  422. #ifndef NO_GETKEY
  423.                 puts("all");
  424. #endif
  425.                 global_opts |= OPT_REPLACEALL;
  426.                 dothis= 1;
  427.                 break;
  428.               case 'q': case 'Q':
  429. #ifndef NO_GETKEY
  430.                 puts("quit");
  431. #endif
  432.                 dothis= 0;
  433.                 err= 1;     /* positive error codes are not ignored */
  434.                 break;
  435.               default:
  436.                 dothis= -1; /* hack: means 'invalid choice' */
  437.                 break;
  438.             }
  439.  
  440.           } while( dothis < 0 );
  441.         }
  442.       }
  443.  
  444.       if(dothis)
  445.       {
  446.         if(!CP_SILENT)
  447.         {
  448.           printf( (CP_DRYRUN ? (strchr(outname,' ') ? "\"%s\" " : "%s ")
  449.                              : "   %s"), outname );
  450.           fflush(stdout);
  451.         }
  452.  
  453.         if(!CP_DRYRUN)
  454.         {
  455.           long n;
  456.           FILE *src, *dst;
  457.  
  458.           if(src= fopen(this->fname,"rb"))
  459.           {
  460.             if(dst= fopen(outname,"wb"))
  461.             {
  462.               if( (n= filecopy(src,dst,0L)) < 0L )
  463.               {
  464.                 if(!CP_SILENT)
  465.                   putchar('\n');
  466.                 perror( (n==(-1L)) ? this->fname : outname );
  467.                 echo("Error copying `%s'.",this->fname);
  468.                 err= n;
  469.               }
  470.               else
  471.               { numbytes+= n;
  472.                 if(!CP_SILENT)
  473.                 { printf("  (%ld)",n);
  474.                   fflush(stdout);
  475.                 }
  476.               }
  477.               fclose(dst);
  478.             }
  479.             else
  480.             { if(!CP_SILENT)
  481.                 putchar('\n');
  482.               echo("Can't write to `%s'.",outname);
  483.               err= -2L;
  484.             }
  485.             fclose(src);
  486.           }
  487.           else
  488.           { if(!CP_SILENT)
  489.               putchar('\n');
  490.             echo("Can't access input file `%s'.",this->fname);
  491.             err= -1L;
  492.           }
  493.         }
  494.  
  495.         if(!err)
  496.         {
  497.           ++numfiles;
  498.  
  499.           if(!CP_SILENT && !CP_DRYRUN)
  500.             putchar('\n');
  501.         }
  502.  
  503.         if(CP_SILENT && !CP_DRYRUN)
  504.         { printf("%d %s copied\r",numfiles, (numfiles==1) ? "file" : "files");
  505.           fflush(stdout);
  506.         }
  507.       }
  508.       free(this->fname);
  509.     }
  510.     free(this);
  511.  
  512.     /* Negative error codes indicate I/O errors which are ignored
  513.      * if w/ the '--keep-going' option given in the command-line.
  514.      * Positive values indicate fatal errors and can be used to
  515.      * force exiting from this function */
  516.  
  517.     if(err<0 && CP_KEEPGOING)
  518.       err= 0L;
  519.   }
  520.  
  521.   if(!CP_DRYRUN)
  522.     (void)fc_setbuf(0L);
  523.  
  524.   if(!CP_DRYRUN || (CP_DRYRUN && CP_SILENT))
  525.     printf("%d %s%s",numfiles, (numfiles==1) ? "file" : "files",
  526.       (CP_DRYRUN) ? ".\n" : " copied.");
  527.  
  528.   if(!CP_DRYRUN)
  529.     printf(" (%ld %s)\n", numbytes, (numbytes==1) ? "byte" : "bytes");
  530.  
  531.   return err;
  532. }
  533.  
  534.  
  535. int prepare_distribution(char *fname, char *pname)
  536. {
  537.   FILE *fp;
  538.   int err= 0;
  539.  
  540.   if( fp= fopen(fname,"r") )
  541.   {
  542.     fnode *flist;
  543.     if( flist= collect_files(fp) )
  544.     {
  545.       err= copy_files(flist, pname);
  546.     }
  547.     fclose(fp);
  548.   }
  549.   else
  550.   { perror(fname);
  551.     echo("Can't access `%s'\n",fname);
  552.     err= 1;
  553.   }
  554.   return err;
  555. }
  556.